/* 
 * File:   NaryOperator.cpp
 * Author: Gab'z
 * 
 * Created on 4 juin 2012, 11:41
 */

#include "NaryOperator.h"
#include "../model/memento/StackMemento.h"
#include "../model/command/literal/Constant.h"
#include "../model/command/literal/Expression.h"
#include "../model/Context.h"
#include "model/command/literal/Integer.h"
#include "model/command/literal/Complex.h"

namespace calculator {

    NaryOperator::NaryOperator(const std::string name) : Operator(name) {
    }

    NaryOperator::NaryOperator(const NaryOperator& orig) :
    Operator(orig._name) {
    }

    NaryOperator::~NaryOperator() {
    }

    std::string NaryOperator::isExecutable() const {
        // le premier nombre de la pile doit être un entier,
        if (Context::getInstance()->getCurrentStack()->size() == 0) return "Empty stack";
        const Integer* integer = dynamic_cast<const Integer*> (Context::getInstance()->getCurrentStack()->top());
        if (integer == 0 || (int) (integer->value()) < 1) return "Stack top must be a positive Integer";
        // on doit s'assurer qu'il y ait assez de constantes dans la pile
        if ((Context::getInstance()->getCurrentStack()->size() - 1) < integer->value()) {
            std::ostringstream oss;
            oss << "Not enough numbers in stack (";
            oss << (Context::getInstance()->getCurrentStack()->size() - 1);
            oss << "/" << (int) (integer->value()) << ")";
            return oss.str();
        }
        // on vérifie le paramètre ComplexMode
        for (unsigned int i = 1;
                i <= (unsigned int) (integer->value());
                i++) {
            const Complex* complex = dynamic_cast<const Complex*> (Context::getInstance()->getCurrentStack()->getConstant(i));
            if (complex != 0 && Context::getInstance()->getParameters()->getComplexMode() == false) return "Complex mode OFF";
        }
        return "";
    }

    const Memento* NaryOperator::createMemento() const throw (CommandException) {
        Stack* stack = new Stack();
        if (Context::getInstance()->getCurrentStack()->size() == 0) throw CommandException("Empty stack");
        const Integer* integer = dynamic_cast<const Integer*> (Context::getInstance()->getCurrentStack()->top());
        if (integer == 0) throw CommandException("Stack top must be an Integer");

        if ((int) (integer->value()) < 1) throw CommandException("Stack top value must be a positive Integer");

        if ((Context::getInstance()->getCurrentStack()->size() - 1) < integer->value()) {
            std::stringstream sstr;
            sstr << "Not enough Constant in current stack (";
            sstr << (Context::getInstance()->getCurrentStack()->size() - 1);
            sstr << "/" << ((int) integer->value()) << ")";
            throw CommandException(sstr.str());
        }
        for (unsigned int i = 0; i <= (unsigned int) (integer->value()); i++) {
            stack->push(Context::getInstance()->getCurrentStack()->getConstant(i));
        }
        return new StackMemento(clone(), stack);
    }

    void NaryOperator::eval(Stack* stack) const throw (CommandException) {
        if (stack->size() == 0) throw CommandException("Empty stack");
        const Integer * integerNbOperands = dynamic_cast<const Integer*> (stack->top());
        if (integerNbOperands == 0) throw CommandException("Stack top must be an Integer");
        if ((int) (integerNbOperands->value()) < 1) throw CommandException("Stack top value must >= 1");
        bool containsExpression = false;
        unsigned int i = 1;
        // on cherche si un argument est une expression
        while (!containsExpression && (i <= (unsigned int) (integerNbOperands->value()))) {
            if (dynamic_cast<const Expression*> (stack->getConstant(i)) != 0) containsExpression = true;
            i++;
        }
        const Constant* resConstant;
        if (containsExpression) {
            // une expression est présente
            Expression* resExp = new Expression(stack->getConstant(0));
            for (unsigned int j = 1; j <= (unsigned int) (integerNbOperands->value()); j++) {
                resExp->append(stack->getConstant(j));
            }
            resConstant = resExp;
        } else {
            // pas d'expression, on calcule
            Stack* operandStack = new Stack();
            for (unsigned int j = (unsigned int) (integerNbOperands->value()); j >= 1; j--) {
                operandStack->push(stack->getConstant(j));
            }
            try {
                resConstant = apply(operandStack);
            } catch (ArithmeticException& e) {
                throw;
            }
            delete operandStack;
        }
        int limit = (int) integerNbOperands->value() + 1;
        for (int j = 0; j < limit; j++) {
            stack->pop();
        }

        stack->push(resConstant);
        delete resConstant;
    }
}